﻿namespace Groundwork.Extensions
{
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Formatters.Binary;

    /// <summary>
    /// Generic Type Extensions.
    /// </summary>
    public static class GenericTypes
    {
        /// <summary>
        /// Performs a check on the current type to see if the specified property exists
        /// </summary>
        /// <param name="typeValue">current type</param>
        /// <param name="find">Property to find (by name)</param>
        /// <returns>true indicates the items existence</returns>
        public static bool HasProperty(this object typeValue, string find)
        {
            if (typeValue.GetType().GetProperty(find) != null)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Performs a check on the current type to see if the specified method exists
        /// </summary>
        /// <param name="typeValue">current type</param>
        /// <param name="find">Method to find (by name)</param>
        /// <returns>true indicates the items existence</returns>
        public static bool HasMethod(this object typeValue, string find)
        {
            if (typeValue.GetType().GetMethod(find) != null)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Performs a check on the current type to see if the specified event exists
        /// </summary>
        /// <param name="typeValue">current type</param>
        /// <param name="find">Event to find (by name)</param>
        /// <returns>true indicates the items existence</returns>
        public static bool HasEvent(this object typeValue, string find)
        {
            if (typeValue.GetType().GetEvent(find) != null)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Performs a check on the current type to see if the specified field exists
        /// </summary>
        /// <param name="typeValue">current type</param>
        /// <param name="find">Field to find (by name)</param>
        /// <returns>true indicates the items existence</returns>
        public static bool HasField(this object typeValue, string find)
        {
            if (typeValue.GetType().GetField(find) != null)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Performs a check on the current type to see if the specified attribute exists
        /// </summary>
        /// <typeparam name="T">Type of the attribute to find</typeparam>
        /// <param name="typeValue">current type</param>
        /// <returns>true indicates the items existence</returns>
        public static bool HasAttribute<T>(this object typeValue)
        {
            object[] attribs = typeValue.GetType().GetCustomAttributes(true);
            for (int i = 0; i < attribs.Length; i++)
            {
                object item = attribs[i];
                if (item.GetType() == typeof(T))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Gets the attributes specified by the type parameter
        /// </summary>
        /// <typeparam name="T">Type of attribute to find</typeparam>
        /// <param name="typeValue">current type</param>
        /// <returns>List of attributes</returns>
        public static IList<T> GetAttributes<T>(this object typeValue)
        {
            SortedList<int, T> itemsFound = new SortedList<int, T>();
           
            object[] attribs = typeValue.GetType().GetCustomAttributes(true);
            for (int i = 0; i < attribs.Length; i++)
            {
                object item = attribs[i];
                if (item.GetType() == typeof(T))
                {                   
                    itemsFound.Add(i,(T)item);
                }
            }

            var orderedList = itemsFound.OrderByDescending(k => k.Key);
            return orderedList.Select(k => k.Value).ToList();
        }

        /// <summary>
        /// Performs a deep clone of the selected object.
        /// </summary>
        /// <typeparam name="T">Type to clone</typeparam>
        /// <param name="item">The item.</param>
        /// <returns>Returns a copy of the original object</returns>
        public static T Clone<T>(this object item)
        {
            if (item != null)
            {
                BinaryFormatter formatter = new BinaryFormatter();
                MemoryStream stream = new MemoryStream();

                formatter.Serialize(stream, item);
                stream.Seek(0, SeekOrigin.Begin);

                T result = (T)formatter.Deserialize(stream);

                stream.Close();

                return result;
            }
            else
            {
            	return default(T);
            }
        }
    }
}
